//
// Copyright (c) 2009-2011, chunquedong
//
// This file is part of ChunMap project
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
//
// History:
//   2011-05-02  Jed Young  Creation
//

**
** Well-Know-Text Reader
**
@Js
internal const class WktReader
{
  Geometry read(Str text)
  {
    try
    {
      tIndex := text.index("(")
      type := text[0..<tIndex].trim
      str := text[tIndex+1..-1].trim

      switch (type.upper)
      {
        case "POINT":
          return getPoint(str)
        case "LINESTRING":
          return getLineString(str)
        case "POLYGON":
          return getPolygon(str)
        case "MULTIPOINT":
          return getMultiPoint(str)
        case "MULTILINESTRING":
          return getMultiPath(str)
        case "MULTIPOLYGON":
          return getMultiPolygon(str)
        case "GEOMETRYCOLLECTION":
          return getMultiGeometry(text)
        default:
          throw ParseErr("unknow type")
      }
    }
    catch(Err e)
    {
      //throw ParseErr("WTK Parse error")
      throw e
    }
  }

  Envelope readEnvelope(Str text)
  {
    tIndex := text.index("(")
    type := text[0..<tIndex]
    str := text[tIndex+1..-1].trim

    if (type.equalsIgnoreCase("BOX")) return geEnvelop(str)
    else throw ParseErr("envelop parse error")
  }

//////////////////////////////////////////////////////////////////////////
// sub Method
//////////////////////////////////////////////////////////////////////////

  private GeoPoint getPoint(Str text)
  {
    text = removeBrackets(text)
    return GeoPoint.makeCoord(readCoord(text))
  }

  private LineString getLineString(Str text)
  {
    text = removeBrackets(text)
    return LineString.makePoints(readPointList(text))
  }

  private Polygon getPolygon(Str text)
  {
    Str[] strs := Regex<|\),\(|>.split(text)

    Ring[] rs := strs.map |s->Ring| { getLineString(s).toLinearRing() }
    Ring[] holes := rs[1..-1]
    return Polygon(rs.first, holes)
  }

  private MultiPoint getMultiPoint(Str text)
  {
    text = removeBrackets(text)
    return MultiPoint(readPointList(text).map |p->GeoPoint| { GeoPoint.makeCoord(p) })
  }

  private MultiLineString getMultiPath(Str text)
  {
    Str[] strs := Regex<|\),\(|>.split(text)

    Ring[] rs := strs.map |s->LineString| { getLineString(s) }
    return MultiLineString(rs)
  }

  private MultiPolygon getMultiPolygon(Str text)
  {
    Str[] strs := Regex<|\)\),\(\(|>.split(text)

    Polygon[] pgs := strs.map |s->Polygon| { getPolygon(s) }
    return MultiPolygon(pgs)
  }

  private GeometryCollection getMultiGeometry(Str text)
  {
    text = text.trim
    geos := Geometry[,]

    matcher := Regex<|,[A-Z]|>.matcher(text)

    i := 0
    j := 0
    while (matcher.find) {
      j = matcher.start
      s := text[i..<j]
      i = j + 1

      //echo(s)
      geos.add(read(s))
    }
    if (i != 0)
    {
      last := text[i..-1]
      //echo(last)
      geos.add(read(last))
    }
    return GeometryCollection(geos)
  }

  private Envelope geEnvelop(Str text)
  {
    strs := removeBrackets(text).split(',')

    point1 := readCoord(strs[0])
    point2 := readCoord(strs[1])

    return Envelope(point1.x, point1.y, point2.x, point2.y)
  }

//////////////////////////////////////////////////////////////////////////
// helper
//////////////////////////////////////////////////////////////////////////

  private Point readCoord(Str text)
  {
    coordinate := text.split
    x := Float(coordinate[0])
    y := Float(coordinate[1])

    return Coord(x, y)
  }

  private Point[] readPointList(Str text)
  {
    strs := text.trim.split(',')
    return strs.map |s| { readCoord(s) }
  }

  private Str removeBrackets(Str text)
  {
    text = text.replace("(", " ")
    text = text.replace(")", " ")
    return text.trim
  }
}

